﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace K.Client
{
    public class NetMgr
    {
        private const int MSG_LIST_LEN = 10000;
        private const int RECV_BUF_LEN = 11 * 1024 * 1024;
        public delegate int MsgSvrDispatcher(Msg msg);

        public Msg[] recvMsgBufList;
        public Msg[] sendMsgBufList;
        public Msg[] recvMsgList;
        private Msg[] sendMsgList;

        public int recvMsgBufCount;
        public int sendMsgBufCount;
        public int recvMsgCount;
        private int sendMsgCount;

        private bool mStartIOThread;
        private byte[] recvDataBuf;
        private List<Msg> recvTempMsgList;
        public string MgrName { get; set; }

        private int recvBeginIdx;
        private int recvEndIdx;
        private int recvMsgPacketLen;
        private int recvMsgContentLen;

        private TcpClient mTransceiver;
        private NetworkStream mStream;
        private Thread mRecvThread;
        private Thread mSendThread;

        public NetMgr(bool bStartIOThread)
        {
            recvMsgBufList = new Msg[MSG_LIST_LEN];
            sendMsgBufList = new Msg[MSG_LIST_LEN];
            recvMsgList = new Msg[MSG_LIST_LEN];
            sendMsgList = new Msg[MSG_LIST_LEN];
            recvDataBuf = new byte[RECV_BUF_LEN];
            recvTempMsgList = new List<Msg>();

            recvMsgBufCount = 0;
            sendMsgBufCount = 0;
            recvMsgCount = 0;
            sendMsgCount = 0;

            MgrName = "";
            recvBeginIdx = 0;
            recvEndIdx = 0;
            recvMsgContentLen = 0;
            mStartIOThread = bStartIOThread;
        }

        public void Connect(string ip, int port)
        {
            IPAddress ipAddress = IPAddress.Parse(ip);
            mTransceiver = new TcpClient();
            mTransceiver.ReceiveBufferSize = RECV_BUF_LEN;
            mTransceiver.SendBufferSize = RECV_BUF_LEN;
            mTransceiver.NoDelay = true;
            mTransceiver.Connect(ipAddress, port);
            mStream = mTransceiver.GetStream();

            if (mStartIOThread)
            {
                mRecvThread = new Thread(StartRecvData);
                mRecvThread.Name = MgrName + "接收线程";
                mRecvThread.Start();

                mSendThread = new Thread(StartSendData);
                mSendThread.Name = MgrName + "发送线程";
                mSendThread.Start();
            }
        }

        void StartRecvData()
        {
            for (; ; )
            {
                RecvData();
            }
        }

        void RecvData()
        {
            int count = 0;
            if (mStartIOThread)
            {
                count = mStream.Read(recvDataBuf, recvEndIdx, recvDataBuf.Length - recvEndIdx);
            }
            else
            {
                if (mStream.DataAvailable)
                {
                    count = mStream.Read(recvDataBuf, recvEndIdx, recvDataBuf.Length - recvEndIdx);
                }
            }

            if (count > 0)
            {
                recvEndIdx += count;
                int dataLen = recvEndIdx - recvBeginIdx;
                for (; ; )
                {
                    if (recvMsgContentLen == 0)
                    {
                        if (dataLen < 4)
                        {
                            break;
                        }
                        recvMsgPacketLen = BitConverter.ToInt32(recvDataBuf, recvBeginIdx);
                        recvBeginIdx += 4; dataLen = recvEndIdx - recvBeginIdx;
                        recvMsgContentLen = recvMsgPacketLen - 4;
                    }
                    if (recvMsgContentLen == 0)
                    {
                        continue;
                    }
                    if (recvMsgContentLen > dataLen)
                    {
                        break;
                    }
                    Msg msg = Msg.DeserializeMsg(recvDataBuf, recvBeginIdx, recvMsgContentLen);
                    if (msg == null)
                    {
                        Console.WriteLine("{0} Received packet error,len:{1}", MgrName, recvMsgPacketLen);
                    }
                    else
                    {
                        recvTempMsgList.Add(msg);
                    }
                    recvBeginIdx += recvMsgContentLen;
                    dataLen = recvEndIdx - recvBeginIdx;
                    recvMsgContentLen = 0;
                }
                Array.Copy(recvDataBuf, recvBeginIdx, recvDataBuf, 0, dataLen);
                recvBeginIdx = 0; recvEndIdx = dataLen;

                int newMsgCount = recvTempMsgList.Count;
                if (newMsgCount > 0)
                {
                    Msg[] newMsgList = recvTempMsgList.ToArray();
                    int index = 0;
                    for (; ; )
                    {
                        int copyCount = CopyToRecvMsgBufList(newMsgList, ref index, ref newMsgCount);
                        if (copyCount <= 0)
                        {
                            if (mStartIOThread)
                            {
                                Thread.Sleep(1);
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }
                        if (newMsgCount <= 0)
                        {
                            break;
                        }
                    }
                    recvTempMsgList.Clear();
                }
            }
        }

        private int CopyToRecvMsgBufList(Msg[] newMsgList, ref int index, ref int newMsgCount)
        {
            int count = 0;
            int freeSize = 0;
            lock (recvMsgBufList)
            {
                freeSize = recvMsgBufList.Length - recvMsgBufCount;
                if (freeSize > 0)
                {
                    if (newMsgCount <= freeSize)
                    {
                        Array.Copy(newMsgList, index, recvMsgBufList, recvMsgBufCount, newMsgCount);
                        recvMsgBufCount += newMsgCount;
                        index += newMsgCount;
                        count = newMsgCount;
                        newMsgCount -= newMsgCount;
                    }
                    else
                    {
                        Array.Copy(newMsgList, index, recvMsgBufList, recvMsgBufCount, freeSize);
                        recvMsgBufCount += freeSize;
                        index += freeSize;
                        count = freeSize;
                        newMsgCount -= freeSize;
                    }
                }
                else
                {
                    count = 0;
                }
            }
            return count;
        }

        public int RecvMsg(int nMaxProcessCount, MsgSvrDispatcher vDispatcher)
        {
            if (!mStartIOThread)
            {
                RecvData();
            }
            int recvCount = 0;
            int freeSize = MSG_LIST_LEN - recvMsgCount;
            if (freeSize > 0)
            {
                lock (recvMsgBufList)
                {
                    if (recvMsgBufCount > 0)
                    {
                        if (recvMsgBufCount <= freeSize)
                        {
                            Array.Copy(recvMsgBufList, 0, recvMsgList, recvMsgCount, recvMsgBufCount);
                            recvCount = recvMsgBufCount;
                            recvMsgCount += recvMsgBufCount;
                            recvMsgBufCount = 0;
                        }
                        else
                        {
                            Array.Copy(recvMsgBufList, 0, recvMsgList, recvMsgCount, freeSize);
                            recvCount = freeSize;
                            recvMsgCount += freeSize;
                            recvMsgBufCount -= freeSize;
                            Array.Copy(recvMsgBufList, freeSize, recvMsgBufList, 0, recvMsgBufCount);
                        }
                    }
                }
            }
            Msg[] msgList = recvMsgList;
            int processCount = 0;
            if (recvMsgCount > nMaxProcessCount)
            {
                processCount = nMaxProcessCount;
            }
            else
            {
                processCount = recvMsgCount;
            }
            for (int i = 0; i < processCount; i++)
            {
                vDispatcher(msgList[i]);
            }
            recvMsgCount -= processCount;
            if (recvMsgCount > 0)
            {
                Array.Copy(recvMsgList, processCount, recvMsgList, 0, recvMsgCount);
            }
            return processCount;
        }

        void StartSendData()
        {
            for (; ; )
            {
                SendData();
                if (sendMsgCount == 0)
                {
                    Thread.Sleep(1);
                }
            }
        }

        void SendData()
        {
            sendMsgCount = 0;
            lock (sendMsgBufList)
            {
                if (sendMsgBufCount > 0)
                {
                    Array.Copy(sendMsgBufList, sendMsgList, sendMsgBufCount);
                    sendMsgCount = sendMsgBufCount;
                    sendMsgBufCount = 0;
                }
            }

            for (int i = 0; i < sendMsgCount; i++)
            {
                byte[] data = Msg.SerializeMsg(sendMsgList[i]);
                if (data == null)
                {
                    Console.WriteLine("{0} Send msg to {1}({2}) fail,id:{3}(0x{3:X8})",
                        MgrName, sendMsgList[i].head.DstEntity, sendMsgList[i].head.DstID, sendMsgList[i].id);
                    continue;
                }
                mStream.Write(data, 0, data.Length);
            }
        }

        public void SendMsg(Msg msg)
        {
            for (; ; )
            {
                lock (sendMsgBufList)
                {
                    if (sendMsgBufCount < MSG_LIST_LEN)
                    {
                        sendMsgBufList[sendMsgBufCount] = msg;
                        sendMsgBufCount++;
                        break;
                    }
                    else
                    {
                        if (!mStartIOThread)
                        {
                            break;
                        }
                    }
                }

            }
            if (!mStartIOThread)
            {
                SendData();
            }
        }
    }
}